﻿/* 
 * Copyright 2010 Justin Pealing
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

using System;
using System.Collections.Generic;
using Minnow.Web.CGI;
using Minnow.Web.Http;
using Minnow.Web.Native;
using System.IO;

namespace Minnow.Web
{
    /// <summary>
    /// Handles CGI http requests.
    /// </summary>
    public class CgiHandler : RequestHandler
    {
        /// <summary>
        /// Initializes a new instance of <see cref="Minnow.Web.CgiHandler" />
        /// </summary>
        public CgiHandler()
        {

        }

        /// <summary>
        /// Initializes a new instance of <see cref="Minnow.Web.CgiHandler"/>
        /// </summary>
        /// <param name="Executable">The path to the CGI executable to run.</param>
        public CgiHandler(string Executable)
        {
            this.Executable = Executable;
        }

        /// <summary>
        /// Gets and sets the path to the CGI executable to run.
        /// </summary>
        /// <remarks>
        /// Set this to null for CGI scripts which are able to run on their own (e.g. binaries).
        /// </remarks>
        public string Executable
        {
            get;
            set;
        }

        /// <summary>
        /// Initialises the CGI process with start information (command line arguments, environment variables etc...)
        /// </summary>
        /// <param name="Request">Request object to initialise the CGI process from</param>
        /// <returns>CGI Process</returns>
        private CreateProcess ConstructProcess(IHttpRequest Request, string FilePath)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.CommandLine = string.IsNullOrEmpty(this.Executable) ? FilePath : this.Executable;
            startInfo.WorkingDirectory = this.LocalRoot;
            startInfo.CreateNoWindow = true;

            Dictionary<string, string> environmentVariables = CGIUtils.GetEnvironmentVariables(Request, FilePath);
            foreach (KeyValuePair<string, string> variable in environmentVariables)
            {
                startInfo.EnvironmentVariables[variable.Key] = variable.Value;
            }

            return CreateProcess.Start(startInfo);
        }

        #region IHttpHandler Members

        public override void Process(IHttpRequest Request, IHttpResponse Response, string FilePath)
        {
            CreateProcess process = this.ConstructProcess(Request, FilePath);

            StreamUtils.CopyStream(Request.InputStream, process.StandardInput.BaseStream);
            process.StandardInput.Flush();

            // TODO: Improve this handling (what happens if one of these reads blocks?)
            StreamReader standardOutput;
            using (MemoryStream buffer = new MemoryStream())
            {
                StreamUtils.CopyStream(process.StandardOutput.BaseStream, buffer);
                standardOutput = new StreamReader(new MemoryStream(buffer.ToArray()));
            }

            StreamReader standardError;
            using (MemoryStream buffer = new MemoryStream())
            {
                StreamUtils.CopyStream(process.StandardError.BaseStream, buffer);
                standardError = new StreamReader(new MemoryStream(buffer.ToArray()));
            }

            process.WaitForExit();
            CGIUtils.ParseCgiResponse(Response, standardOutput, standardError, process.ExitCode);
        }
        #endregion
    }
}